package dproxy.server.net.protocol;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dproxy.server.common.exceptions.AppInfrastructureException;
import dproxy.server.common.exceptions.ResponseReadException;
import dproxy.server.common.util.UtilConvert;
import dproxy.server.net.backoff.Timeout;

/**
 * Reader for HTTP Protocol. 
 * This class was created for testing purposes and is not 
 * based on HTTP protocol. This should be changed if expected 
 * to correctly work as a real HTTP proxy.
 */
public class HttpProtocol extends BaseProtocol {

    /** Stream buffer lenght. */
    private static final int READ_BUFFER_SIZE = 512;

    /** Logger. */
    private Log logger = LogFactory.getLog(getClass());
    
    /**
     * @see dproxy.server.net.protocol.Protocol#getName()
     */
    public String getName() {
        return "HTTP";
    }

    /**
     * @see dproxy.server.net.protocol.BaseProtocol#init()
     */
    @Override
    public void init() {
        super.init();
        if (logger.isDebugEnabled()) {
            logger.debug("Initiating HTTP Protocol");
        }
    }

    /**
     * @see dproxy.server.net.protocol.Protocol#generateKey(byte[])
     */
    public String generateKey(byte[] request) {
        return createSimpleKey(request);
    }

    /**
     * @see dproxy.server.net.protocol.Protocol#readRequest(InputStream)
     */
    public byte[] readRequest(InputStream inputStream) {
        byte[] bs;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            while (true) {
                // Read a set of characters from the socket
                int i;
                byte[] buffer = new byte[READ_BUFFER_SIZE];
                i = inputStream.read(buffer);
                for (int j=0; j<i; j++) {
                    baos.write((byte)buffer[j]);
                }
                if (inputStream.available() == 0) {
                    // TO BE CHANGED: Will not wait for more data to be available
                    break;
                }
            }
        } catch (IOException e) {
            throw new AppInfrastructureException(e);
        }
        bs = baos.toByteArray();
        return bs;
    }

    /**
     * @see dproxy.server.net.protocol.Protocol#readResponse(InputStream)
     */
    public byte[] readResponse(InputStream inputStream)
            throws ResponseReadException {
        byte[] bs;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            while (true) {
                // Read a set of characters from the socket
                int i;
                byte[] buffer = new byte[READ_BUFFER_SIZE];
                i = inputStream.read(buffer);
                for (int j=0; j<i; j++) {
                    baos.write((byte)buffer[j]);
                }
                if (inputStream.available() == 0) {
                    String streamContents = UtilConvert.convertToString(baos.toByteArray());
                    int contentLenght = -1;
                    if (streamContents.indexOf("HTTP/1.1 200 OK") > 0) {
                        // If its a 200 OK check content lenght
                        StringTokenizer headerTokenizer = new StringTokenizer(streamContents, "\n");
                        while (headerTokenizer.hasMoreTokens()) {
                            String header = headerTokenizer.nextToken();
                            if (header.indexOf("Content-Length") > 0) {
                                StringTokenizer clTokenizer = new StringTokenizer(header, ":");
                                // Throw away header
                                clTokenizer.nextToken();
                                contentLenght = Integer.parseInt(clTokenizer.nextToken().trim());
                            }
                        }
                        if (contentLenght != -1) {
                            if (streamContents.length() > contentLenght) {
                                // Ok. Got enuf data
                                break;
                            } else {
                                // wait more
                                Timeout.getInstance().waitForDataAvailable(inputStream);
                            }
                        } else {
                            // wait more
                            Timeout.getInstance().waitForDataAvailable(inputStream);
                        }
                    } else {
                        // Is not a 200 OK, simply stops reading
                        break;
                    }
                }
            }
        } catch (IOException e) {
            throw new AppInfrastructureException(e);
        }
        bs = baos.toByteArray();
        return bs;
    }
    
}
